home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
pcl
/
pcl-rev4.lha
/
std-class.lisp
< prev
next >
Wrap
Lisp/Scheme
|
1990-12-05
|
34KB
|
978 lines
;;;-*-Mode:LISP; Package:PCL; Base:10; Syntax:Common-lisp -*-
;;;
;;; *************************************************************************
;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
;;; All rights reserved.
;;;
;;; Use and copying of this software and preparation of derivative works
;;; based upon this software are permitted. Any distribution of this
;;; software or derivative works must comply with all applicable United
;;; States export control laws.
;;;
;;; This software is made available AS IS, and Xerox Corporation makes no
;;; warranty about the software, its performance or its conformity to any
;;; specification.
;;;
;;; Any person obtaining a copy of this software is requested to send their
;;; name and post office or electronic mail address to:
;;; CommonLoops Coordinator
;;; Xerox PARC
;;; 3333 Coyote Hill Rd.
;;; Palo Alto, CA 94304
;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
;;;
;;; Suggestions, comments and requests for improvements are also welcome.
;;; *************************************************************************
;;;
(in-package 'pcl)
(define-gf-predicate classp class)
(define-gf-predicate standard-class-p standard-class)
(define-gf-predicate forward-referenced-class-p forward-referenced-class)
(defmethod shared-initialize :after ((object documentation-mixin)
slot-names
&key documentation)
(declare (ignore slot-names))
(setf (plist-value object 'documentation) documentation))
(defmethod documentation (object &optional doc-type)
(lisp:documentation object doc-type))
(defmethod (setf documentation) (new-value object &optional doc-type)
(declare (ignore new-value doc-type))
(error "Can't change the documentation of ~S." object))
(defmethod documentation ((object documentation-mixin) &optional doc-type)
(declare (ignore doc-type))
(plist-value object 'documentation))
(defmethod (setf documentation) (new-value (object documentation-mixin) &optional doc-type)
(declare (ignore doc-type))
(setf (plist-value object 'documentation) new-value))
(defmethod documentation ((slotd standard-slot-definition) &optional doc-type)
(declare (ignore doc-type))
(slot-value slotd 'documentation))
(defmethod (setf documentation) (new-value (slotd standard-slot-definition) &optional doc-type)
(declare (ignore doc-type))
(setf (slot-value slotd 'documentation) new-value))
;;;
;;; Various class accessors that are a little more complicated than can be
;;; done with automatically generated reader methods.
;;;
(defmethod class-wrapper ((class pcl-class))
(with-slots (wrapper) class
(let ((w? wrapper))
(if (consp w?)
(let ((new (make-wrapper class)))
(setf (wrapper-instance-slots-layout new) (car w?)
(wrapper-class-slots new) (cdr w?))
(setq wrapper new))
w?))))
(defmethod class-precedence-list ((class pcl-class))
(unless (class-finalized-p class) (finalize-inheritance class))
(with-slots (class-precedence-list) class class-precedence-list))
(defmethod class-finalized-p ((class pcl-class))
(with-slots (wrapper) class (not (null wrapper))))
(defmethod class-prototype ((class std-class))
(with-slots (prototype) class
(or prototype (setq prototype (allocate-instance class)))))
(defmethod class-direct-default-initargs ((class std-class))
(plist-value class 'direct-default-initargs))
(defmethod class-default-initargs ((class std-class))
(plist-value class 'default-initargs))
(defmethod class-constructors ((class std-class))
(plist-value class 'constructors))
(defmethod class-slot-cells ((class std-class))
(plist-value class 'class-slot-cells))
;;;
;;; Class accessors that are even a little bit more complicated than those
;;; above. These have a protocol for updating them, we must implement that
;;; protocol.
;;;
;;;
;;; Maintaining the direct subclasses backpointers. The update methods are
;;; here, the values are read by an automatically generated reader method.
;;;
(defmethod add-direct-subclass ((class class) (subclass class))
(with-slots (direct-subclasses) class
(pushnew subclass direct-subclasses)
subclass))
(defmethod remove-direct-subclass ((class class) (subclass class))
(with-slots (direct-subclasses) class
(setq direct-subclasses (remove subclass direct-subclasses))
subclass))
;;;
;;; Maintaining the direct-methods and direct-generic-functions backpointers.
;;;
;;; There are four generic functions involved, each has one method for the
;;; class case and another method for the damned EQL specializers. All of
;;; these are specified methods and appear in their specified place in the
;;; class graph.
;;;
;;; ADD-METHOD-ON-SPECIALIZER
;;; REMOVE-METHOD-ON-SPECIALIZER
;;; SPECIALIZER-METHODS
;;; SPECIALIZER-GENERIC-FUNCTIONS
;;;
;;; In each case, we maintain one value which is a cons. The car is the list
;;; methods. The cdr is a list of the generic functions. The cdr is always
;;; computed lazily.
;;;
(defmethod add-method-on-specializer ((method method) (specializer class))
(with-slots (direct-methods) specializer
(setf (car direct-methods) (adjoin method (car direct-methods)) ;PUSH
(cdr direct-methods) ()))
method)
(defmethod remove-method-on-specializer ((method method) (specializer class))
(with-slots (direct-methods) specializer
(setf (car direct-methods) (remove method (car direct-methods))
(cdr direct-methods) ()))
method)
(defmethod specializer-methods ((specializer class))
(with-slots (direct-methods) specializer
(car direct-methods)))
(defmethod specializer-generic-functions ((specializer class))
(with-slots (direct-methods) specializer
(or (cdr direct-methods)
(setf (cdr direct-methods)
(gathering1 (collecting-once)
(dolist (m (car direct-methods))
(gather1 (method-generic-function m))))))))
;;;
;;; This hash table is used to store the direct methods and direct generic
;;; functions of EQL specializers. Each value in the table is the cons.
;;;
(defvar *eql-specializer-methods* (make-hash-table :test #'eql))
(defmethod add-method-on-specializer ((method method) (specializer eql-specializer))
(let* ((object (eql-specializer-object specializer))
(entry (gethash object *eql-specializer-methods*)))
(unless entry
(setq entry
(setf (gethash object *eql-specializer-methods*)
(cons nil nil))))
(setf (car entry) (adjoin method (car entry))
(cdr entry) ())
method))
(defmethod remove-method-on-specializer ((method method) (specializer eql-specializer))
(let* ((object (eql-specializer-object specializer))
(entry (gethash object *eql-specializer-methods*)))
(when entry
(setf (car entry) (remove method (car entry))
(cdr entry) ()))
method))
(defmethod specializer-methods ((specializer eql-specializer))
(car (gethash (eql-specializer-object specializer) *eql-specializer-methods*)))
(defmethod specializer-generic-functions ((specializer eql-specializer))
(let* ((object (eql-specializer-object specializer))
(entry (gethash object *eql-specializer-methods*)))
(when entry
(or (cdr entry)
(setf (cdr entry)
(gathering1 (collecting-once)
(dolist (m (car entry))
(gather1 (method-generic-function m)))))))))
(defun real-load-defclass (name metaclass-name supers slots other accessors)
(do-standard-defsetfs-for-defclass accessors) ;***
(apply #'ensure-class name :metaclass metaclass-name
:direct-superclasses supers
:direct-slots slots
:definition-source `((defclass ,name)
,(load-truename))
other))
(defun ensure-class (name &rest all)
(apply #'ensure-class-using-class name (find-class name nil) all))
(defmethod ensure-class-using-class (name (class null) &rest args &key)
(multiple-value-bind (meta initargs)
(ensure-class-values class args)
(setf class (apply #'make-instance meta :name name initargs)
(find-class name) class)
(inform-type-system-about-class class name) ;***
class))
(defmethod ensure-class-using-class (name (class pcl-class) &rest args &key)
(multiple-value-bind (meta initargs)
(ensure-class-values class args)
(unless (eq (class-of class) meta) (change-class class meta))
(apply #'reinitialize-instance class initargs)
(inform-type-system-about-class class name) ;***
class))
(defun ensure-class-values (class args)
(let* ((initargs (copy-list args))
(unsupplied (list 1))
(supplied-meta (getf initargs :metaclass unsupplied))
(supplied-supers (getf initargs :direct-superclasses unsupplied))
(supplied-slots (getf initargs :direct-slots unsupplied))
(meta
(cond ((neq supplied-meta unsupplied)
(find-class supplied-meta))
((or (null class)
(forward-referenced-class-p class))
*the-class-standard-class*)
(t
(class-of class))))
(proto (class-prototype meta)))
(flet ((fix-super (s)
(cond ((classp s) s)
((not (legal-class-name-p s))
(error "~S is not a class or a legal class name." s))
(t
(or (find-class s nil)
(setf (find-class s)
(make-instance 'forward-referenced-class
:name s)))))))
(loop (unless (remf initargs :metaclass) (return)))
(loop (unless (remf initargs :direct-superclasses) (return)))
(loop (unless (remf initargs :direct-slots) (return)))
(values meta
(list* :direct-superclasses
(and (neq supplied-supers unsupplied)
(mapcar #'fix-super supplied-supers))
:direct-slots
(and (neq supplied-slots unsupplied) supplied-slots)
initargs)))))
;;;
;;;
;;;
(defmethod shared-initialize :before ((class std-class)
slot-names
&key direct-superclasses)
(declare (ignore slot-names))
;; *** error checking
)
(defmethod shared-initialize :after
((class std-class)
slot-names
&key (direct-superclasses nil direct-superclasses-p)
(direct-slots nil direct-slots-p)
(direct-default-initargs nil direct-default-initargs-p))
(declare (ignore slot-names))
(setq direct-superclasses
(if direct-superclasses-p
(setf (slot-value class 'direct-superclasses)
(or direct-superclasses
(list *the-class-standard-object*)))
(slot-value class 'direct-superclasses)))
(setq direct-slots
(if direct-slots-p
(setf (slot-value class 'direct-slots)
(mapcar #'(lambda (pl) (make-direct-slotd class pl)) direct-slots))
(slot-value class 'direct-slots)))
(if direct-default-initargs-p
(setf (plist-value class 'direct-default-initargs) direct-default-initargs)
(setq direct-default-initargs (plist-value class 'direct-default-initargs)))
(setf (plist-value class 'class-slot-cells)
(gathering1 (collecting)
(dolist (dslotd direct-slots)
(when (eq (slotd-allocation dslotd) class)
(let ((initfunction (slotd-initfunction dslotd)))
(gather1 (cons (slotd-name dslotd)
(if initfunction (funcall initfunction) *slot-unbound*))))))))
(add-direct-subclasses class direct-superclasses)
(add-slot-accessors class direct-slots))
(defmethod reinitialize-instance :before ((class std-class)
&key direct-superclasses
direct-slots
direct-default-initargs)
(declare (ignore direct-default-initargs))
(remove-direct-subclasses class (class-direct-superclasses class))
(remove-slot-accessors class (class-direct-slots class)))
(defmethod reinitialize-instance :after ((class std-class)
&rest initargs
&key)
(update-class class nil)
(map-dependents class
#'(lambda (dependent)
(apply #'update-dependent class dependent initargs))))
(defun add-slot-accessors (class dslotds)
(fix-slot-accessors class dslotds 'add))
(defun remove-slot-accessors (class dslotds)
(fix-slot-accessors class dslotds 'remove))
(defun fix-slot-accessors (class dslotds add/remove)
(flet ((fix (gfspec name r/w)
(let ((gf (ensure-generic-function gfspec)))
(case r/w
(r (if (eq add/remove 'add)
(add-reader-method class gf name)
(remove-reader-method class gf)))
(w (if (eq add/remove 'add)
(add-writer-method class gf name)
(remove-writer-method class gf)))))))
(dolist (dslotd dslotds)
(let ((slot-name (slotd-name dslotd)))
(dolist (r (slotd-readers dslotd)) (fix r slot-name 'r))
(dolist (w (slotd-writers dslotd)) (fix w slot-name 'w))))))
(defun add-direct-subclasses (class new)
(dolist (n new)
(unless (memq class (class-direct-subclasses class))
(add-direct-subclass n class))))
(defun remove-direct-subclasses (class new)
(let ((old (class-direct-superclasses class)))
(dolist (o (set-difference old new))
(remove-direct-subclass o class))))
;;;
;;;
;;;
(defmethod finalize-inheritance ((class std-class))
(update-class class t))
;;;
;;; Called by :after reinitialize instance whenever a class is reinitialized.
;;; The class may or may not be finalized.
;;;
(defun update-class (class finalizep)
(when (or finalizep (class-finalized-p class))
(let* ((dsupers (class-direct-superclasses class))
(dslotds (class-direct-slots class))
(dinits (class-direct-default-initargs class))
(cpl (compute-class-precedence-list class dsupers))
(eslotds (compute-slots class cpl dslotds))
(inits (compute-default-initargs class cpl dinits)))
(update-cpl class cpl)
(update-slots class cpl eslotds)
(update-inits class inits)
(update-constructors class)))
(unless finalizep
(dolist (sub (class-direct-subclasses class)) (update-class sub nil))))
(defun update-cpl (class cpl)
(when (class-finalized-p class)
(unless (equal (class-precedence-list class) cpl)
(force-cache-flushes class)))
(setf (slot-value class 'class-precedence-list) cpl))
(defun update-slots (class cpl eslotds)
(multiple-value-bind (nlayout nwrapper-class-slots)
(compute-storage-info cpl eslotds)
;;
;; If there is a change in the shape of the instances then the
;; old class is now obsolete.
;;
(let* ((owrapper (class-wrapper class))
(olayout (and owrapper (wrapper-instance-slots-layout owrapper)))
(owrapper-class-slots (and owrapper (wrapper-class-slots owrapper)))
(nwrapper
(cond ((null owrapper)
(make-wrapper class))
((and (equal nlayout olayout)
(not
(iterate ((o (list-elements owrapper-class-slots))
(n (list-elements nwrapper-class-slots)))
(unless (eq (car o) (car n)) (return t)))))
owrapper)
(t
;;
;; This will initialize the new wrapper to have the same
;; state as the old wrapper. We will then have to change
;; that. This may seem like wasted work (it is), but the
;; spec requires that we call make-instances-obsolete.
;;
(make-instances-obsolete class)
(class-wrapper class)))))
(with-slots (wrapper no-of-instance-slots slots) class
(setf no-of-instance-slots (length nlayout)
slots eslotds
(wrapper-instance-slots-layout nwrapper) nlayout
(wrapper-class-slots nwrapper) nwrapper-class-slots
wrapper nwrapper))
(dolist (eslotd eslotds)
(setf (slotd-class eslotd) class)
(setf (slotd-instance-index eslotd)
(instance-slot-index nwrapper (slotd-name eslotd)))))))
(defun compute-storage-info (cpl eslotds)
(let ((instance ())
(class ()))
(dolist (eslotd eslotds)
(let ((alloc (slotd-allocation eslotd)))
(cond ((eq alloc :instance) (push eslotd instance))
((classp alloc) (push eslotd class)))))
(values (compute-layout cpl instance)
(compute-class-slots class))))
(defun compute-layout (cpl instance-eslotds)
(let* ((names
(gathering1 (collecting)
(dolist (eslotd instance-eslotds)
(when (eq (slotd-allocation eslotd) :instance)
(gather1 (slotd-name eslotd))))))
(order ()))
(labels ((rwalk (tail)
(when tail
(rwalk (cdr tail))
(dolist (ss (class-slots (car tail)))
(let ((n (slotd-name ss)))
(when (memq n names)
(setq order (cons n order)
names (remove n names))))))))
(rwalk cpl)
(reverse (append names order)))))
(defun compute-class-slots (eslotds)
(gathering1 (collecting)
(dolist (eslotd eslotds)
(gather1
(assoc (slotd-name eslotd)
(class-slot-cells (slotd-allocation eslotd)))))))
(defun update-inits (class inits)
(setf (plist-value class 'default-initargs) inits))
;;;
;;;
;;;
(defmethod compute-default-initargs ((class std-class) cpl direct)
(labels ((walk (tail)
(if (null tail)
nil
(let ((c (pop tail)))
(append (if (eq c class)
direct
(class-direct-default-initargs c))
(walk tail))))))
(let ((initargs (walk cpl)))
(delete-duplicates initargs :test #'eq :key #'car :from-end t))))
;;;
;;; Protocols for constructing direct and effective slot definitions.
;;;
;;;
;;;
;;;
(defmethod direct-slot-definition-class ((class std-class) initargs)
(declare (ignore initargs))
(find-class 'standard-direct-slot-definition))
(defun make-direct-slotd (class initargs)
(let ((initargs (list* :class class initargs)))
(apply #'make-instance (direct-slot-definition-class class initargs) initargs)))
;;;
;;;
;;;
(defmethod compute-slots ((class std-class) cpl class-direct-slots)
;;
;; As specified, we must call COMPUTE-EFFECTIVE-SLOT-DEFINITION once
;; for each different slot name we find in our superclasses. Each
;; call receives the class and a list of the dslotds with that name.
;; The list is in most-specific-first order.
;;
(let ((name-dslotds-alist ()))
(labels ((collect-one-class (dslotds)
(dolist (d dslotds)
(let* ((name (slotd-name d))
(entry (assq name name-dslotds-alist)))
(if entry
(push d (cdr entry))
(push (list name d) name-dslotds-alist))))))
(collect-one-class class-direct-slots)
(dolist (c (cdr cpl)) (collect-one-class (class-direct-slots c)))
(mapcar #'(lambda (direct)
(compute-effective-slot-definition class
(nreverse (cdr direct))))
name-dslotds-alist))))
(defmethod compute-effective-slot-definition ((class std-class) dslotds)
(let* ((initargs (compute-effective-slot-definition-initargs class dslotds))
(class (effective-slot-definition-class class initargs)))
(apply #'make-instance class initargs)))
(defmethod effective-slot-definition-class ((class std-class) initargs)
(declare (ignore initargs))
(find-class 'standard-effective-slot-definition))
(defmethod compute-effective-slot-definition-initargs
((class std-class) direct-slotds)
(let* ((name nil)
(initfunction nil)
(initform nil)
(initargs nil)
(allocation nil)
(type t)
(namep nil)
(initp nil)
(allocp nil))
(dolist (slotd direct-slotds)
(when slotd
(unless namep
(setq name (slotd-name slotd)
namep t))
(unless initp
(when (slotd-initfunction slotd)
(setq initform (slotd-initform slotd)
initfunction (slotd-initfunction slotd)
initp t)))
(unless allocp
(setq allocation (slotd-allocation slotd)
allocp t))
(setq initargs (append (slotd-initargs slotd) initargs))
(let ((slotd-type (slotd-type slotd)))
(setq type (cond ((null type) slotd-type)
((subtypep type slotd-type) type)
(t `(and ,type ,slotd-type)))))))
(list :name name
:initform initform
:initfunction initfunction
:initargs initargs
:allocation allocation
:type type)))
;;;
;;; NOTE: For bootstrapping considerations, these can't use make-instance
;;; to make the method object. They have to use make-a-method which
;;; is a specially bootstrapped mechanism for making standard methods.
;;;
(defmethod add-reader-method ((class std-class) generic-function slot-name)
(let* ((name (class-name class))
(method (make-a-method 'standard-reader-method
()
(list (or name 'standard-object))
(list class)
(make-reader-method-function class slot-name)
"automatically generated reader method"
slot-name)))
(add-method generic-function method)))
(defmethod add-writer-method ((class std-class) generic-function slot-name)
(let* ((name (class-name class))
(method (make-a-method 'standard-writer-method
()
(list 'new-value (or name 'standard-object))
(list *the-class-t* class)
(make-writer-method-function class slot-name)
"automatically generated writer method"
slot-name)))
(add-method generic-function method)))
(defmethod remove-reader-method ((class std-class) generic-function)
(let ((method (get-method generic-function () (list class) nil)))
(when method (remove-method generic-function method))))
(defmethod remove-writer-method ((class std-class) generic-function)
(let ((method
(get-method generic-function () (list *the-class-t* class) nil)))
(when method (remove-method generic-function method))))
;;;
;;; make-reader-method-function and make-write-method function are NOT part of
;;; the standard protocol. They are however useful, PCL makes uses makes use
;;; of them internally and documents them for PCL users.
;;;
;;; *** This needs work to make type testing by the writer functions which
;;; *** do type testing faster. The idea would be to have one constructor
;;; *** for each possible type test. In order to do this it would be nice
;;; *** to have help from inform-type-system-about-class and friends.
;;;
;;; *** There is a subtle bug here which is going to have to be fixed.
;;; *** Namely, the simplistic use of the template has to be fixed. We
;;; *** have to give the optimize-slot-value method the user might have
;;; *** defined for this metclass a chance to run.
;;;
(defmethod make-reader-method-function ((class standard-class) slot-name)
(make-std-reader-method-function slot-name))
(defmethod make-writer-method-function ((class standard-class) slot-name)
(make-std-writer-method-function slot-name))
(defun make-std-reader-method-function (slot-name)
#'(lambda (instance)
(slot-value instance slot-name)))
(defun make-std-writer-method-function (slot-name)
#'(lambda (nv instance)
(setf (slot-value instance slot-name) nv)))
;;;; inform-type-system-about-class
;;;; make-type-predicate
;;;
;;; These are NOT part of the standard protocol. They are internal mechanism
;;; which PCL uses to *try* and tell the type system about class definitions.
;;; In a more fully integrated implementation of CLOS, the type system would
;;; know about class objects and class names in a more fundamental way and
;;; the mechanism used to inform the type system about new classes would be
;;; different.
;;;
(defmethod inform-type-system-about-class ((class std-class) name)
(let ((predicate-name (make-type-predicate-name name)))
(setf (symbol-function predicate-name) (make-type-predicate name))
(do-satisfies-deftype name predicate-name)))
(defun make-type-predicate (name)
#'(lambda (x)
(not
(null
(memq (find-class name)
(cond ((std-instance-p x)
(class-precedence-list (std-instance-class x)))
((fsc-instance-p x)
(class-precedence-list (fsc-instance-class x)))))))))
;;;
;;; These 4 definitions appear here for bootstrapping reasons. Logically,
;;; they should be in the construct file. For documentation purposes, a
;;; copy of these definitions appears in the construct file. If you change
;;; one of the definitions here, be sure to change the copy there.
;;;
(defvar *initialization-generic-functions*
(list #'make-instance
#'default-initargs
#'allocate-instance
#'initialize-instance
#'shared-initialize))
(defmethod maybe-update-constructors
((generic-function generic-function)
(method method))
(when (memq generic-function *initialization-generic-functions*)
(labels ((recurse (class)
(update-constructors class)
(dolist (subclass (class-direct-subclasses class))
(recurse subclass))))
(when (classp (car (method-specializers method)))
(recurse (car (method-specializers method)))))))
(defmethod update-constructors ((class std-class))
(dolist (cons (class-constructors class))
(install-lazy-constructor-installer cons)))
(defmethod update-constructors ((class class))
())
(defmethod compatible-meta-class-change-p (class proto-new-class)
(eq (class-of class) (class-of proto-new-class)))
(defmethod check-super-metaclass-compatibility ((class t) (new-super t))
(unless (eq (class-of class) (class-of new-super))
(error "The class ~S was specified as a~%super-class of the class ~S;~%~
but the meta-classes ~S and~%~S are incompatible."
new-super class (class-of new-super) (class-of class))))
;;;
;;;
;;;
(defun force-cache-flushes (class)
(let* ((owrapper (class-wrapper class))
(state (wrapper-state owrapper)))
;;
;; We only need to do something if the state is still T. If the
;; state isn't T, it will be FLUSH or OBSOLETE, and both of those
;; will already be doing what we want. In particular, we must be
;; sure we never change an OBSOLETE into a FLUSH since OBSOLETE
;; means do what FLUSH does and then some.
;;
(when (eq state 't)
(let ((nwrapper (make-wrapper class)))
(setf (wrapper-instance-slots-layout nwrapper)
(wrapper-instance-slots-layout owrapper))
(setf (wrapper-class-slots nwrapper)
(wrapper-class-slots owrapper))
(without-interrupts
(setf (slot-value class 'wrapper) nwrapper)
(invalidate-wrapper owrapper 'flush nwrapper))
(update-constructors class))))) ;??? ***
(defun flush-cache-trap (owrapper nwrapper instance)
(declare (ignore owrapper))
(set-wrapper instance nwrapper))
;;;
;;; make-instances-obsolete can be called by user code. It will cause the
;;; next access to the instance (as defined in 88-002R) to trap through the
;;; update-instance-for-redefined-class mechanism.
;;;
(defmethod make-instances-obsolete ((class std-class))
(let ((owrapper (class-wrapper class))
(nwrapper (make-wrapper class)))
(setf (wrapper-instance-slots-layout nwrapper)
(wrapper-instance-slots-layout owrapper))
(setf (wrapper-class-slots nwrapper)
(wrapper-class-slots owrapper))
(without-interrupts
(setf (slot-value class 'wrapper) nwrapper)
(invalidate-wrapper owrapper 'obsolete nwrapper)
class)))
(defmethod make-instances-obsolete ((class symbol))
(make-instances-obsolete (find-class class)))
;;;
;;; obsolete-instance-trap is the internal trap that is called when we see
;;; an obsolete instance. The times when it is called are:
;;; - when the instance is involved in method lookup
;;; - when attempting to access a slot of an instance
;;;
;;; It is not called by class-of, wrapper-of, or any of the low-level instance
;;; access macros.
;;;
;;; Of course these times when it is called are an internal implementation
;;; detail of PCL and are not part of the documented description of when the
;;; obsolete instance update happens. The documented description is as it
;;; appears in 88-002R.
;;;
;;; This has to return the new wrapper, so it counts on all the methods on
;;; obsolete-instance-trap-internal to return the new wrapper. It also does
;;; a little internal error checking to make sure that the traps are only
;;; happening when they should, and that the trap methods are computing
;;; apropriate new wrappers.
;;;
(defun obsolete-instance-trap (owrapper nwrapper instance)
;;
;; local --> local transfer
;; local --> shared discard
;; local --> -- discard
;; shared --> local transfer
;; shared --> shared discard
;; shared --> -- discard
;; -- --> local add
;; -- --> shared --
;;
(let* ((class (wrapper-class nwrapper))
(guts (allocate-instance class)) ;??? allocate-instance ???
(olayout (wrapper-instance-slots-layout owrapper))
(nlayout (wrapper-instance-slots-layout nwrapper))
(oslots (get-slots instance))
(nslots (get-slots guts))
(oclass-slots (wrapper-class-slots owrapper))
(added ())
(discarded ())
(plist ()))
;;
;; Go through all the old local slots.
;;
(iterate ((name (list-elements olayout))
(opos (interval :from 0)))
(let ((npos (posq name nlayout)))
(if npos
(setf (svref nslots npos) (svref oslots opos))
(progn (push name discarded)
(unless (eq (svref oslots opos) *slot-unbound*)
(setf (getf plist name) (svref oslots opos)))))))
;;
;; Go through all the old shared slots.
;;
(iterate ((oclass-slot-and-val (list-elements oclass-slots)))
(let ((name (car oclass-slot-and-val))
(val (cdr oclass-slot-and-val)))
(let ((npos (posq name nlayout)))
(if npos
(setf (svref nslots npos) (cdr oclass-slot-and-val))
(progn (push name discarded)
(unless (eq val *slot-unbound*)
(setf (getf plist name) val)))))))
;;
;; Go through all the new local slots to compute the added slots.
;;
(dolist (nlocal nlayout)
(unless (or (memq nlocal olayout)
(assq nlocal oclass-slots))
(push nlocal added)))
(without-interrupts
(set-wrapper instance nwrapper)
(set-slots instance nslots))
(update-instance-for-redefined-class instance
added
discarded
plist)
nwrapper))
;;;
;;;
;;;
(defmacro change-class-internal (wrapper-fetcher slots-fetcher alloc)
`(let* ((old-class (class-of instance))
(copy (,alloc old-class))
(guts (,alloc new-class))
(new-wrapper (,wrapper-fetcher guts))
(old-wrapper (class-wrapper old-class))
(old-layout (wrapper-instance-slots-layout old-wrapper))
(new-layout (wrapper-instance-slots-layout new-wrapper))
(old-slots (,slots-fetcher instance))
(new-slots (,slots-fetcher guts))
(old-class-slots (wrapper-class-slots old-wrapper)))
;;
;; "The values of local slots specified by both the class Cto and
;; Cfrom are retained. If such a local slot was unbound, it remains
;; unbound."
;;
(iterate ((new-slot (list-elements new-layout))
(new-position (interval :from 0)))
(let ((old-position (position new-slot old-layout :test #'eq)))
(when old-position
(setf (svref new-slots new-position)
(svref old-slots old-position)))))
;;
;; "The values of slots specified as shared in the class Cfrom and
;; as local in the class Cto are retained."
;;
(iterate ((slot-and-val (list-elements old-class-slots)))
(let ((position (position (car slot-and-val) new-layout :test #'eq)))
(when position
(setf (svref new-slots position) (cdr slot-and-val)))))
;; Make the copy point to the old instance's storage, and make the
;; old instance point to the new storage.
(without-interrupts
(setf (,slots-fetcher copy) old-slots)
(setf (,wrapper-fetcher instance) new-wrapper)
(setf (,slots-fetcher instance) new-slots))
(update-instance-for-different-class copy instance)
instance))
(defmethod change-class ((instance standard-object)
(new-class standard-class))
(unless (std-instance-p instance)
(error "Can't change the class of ~S to ~S~@
because it isn't already an instance with metaclass~%~S."
instance
new-class
'standard-class))
(change-class-internal std-instance-wrapper
std-instance-slots
allocate-instance))
(defmethod change-class ((instance standard-object)
(new-class funcallable-standard-class))
(unless (fsc-instance-p instance)
(error "Can't change the class of ~S to ~S~@
because it isn't already an instance with metaclass~%~S."
instance
new-class
'funcallable-standard-class))
(change-class-internal fsc-instance-wrapper
fsc-instance-slots
allocate-instance))
(defmethod change-class ((instance t) (new-class-name symbol))
(change-class instance (find-class new-class-name)))
;;;
;;; The metaclass BUILT-IN-CLASS
;;;
;;; This metaclass is something of a weird creature. By this point, all
;;; instances of it which will exist have been created, and no instance
;;; is ever created by calling MAKE-INSTANCE.
;;;
;;; But, there are other parts of the protcol we must follow and those
;;; definitions appear here.
;;;
(defmethod shared-initialize :before
((class built-in-class) slot-names &rest initargs)
(declare (ignore slot-names))
(error "Attempt to initialize or reinitialize a built in class."))
(defmethod class-direct-slots ((class built-in-class)) ())
(defmethod class-slots ((class built-in-class)) ())
(defmethod class-direct-default-initargs ((class built-in-class)) ())
(defmethod class-default-initargs ((class built-in-class)) ())
(defmethod check-super-metaclass-compatibility ((c class) (s built-in-class))
(or (eq s *the-class-t*)
(error "~S cannot have ~S as a super.~%~
The class ~S is the only built in class that can be a~%~
superclass of a standard class."
c s *the-class-t*)))
;;;
;;;
;;;
(defmethod check-super-metaclass-compatibility ((c std-class)
(f forward-referenced-class))
't)
;;;
;;;
;;;
(defmethod add-dependent ((metaobject dependent-update-mixin) dependent)
(pushnew dependent (plist-value metaobject 'dependents)))
(defmethod remove-dependent ((metaobject dependent-update-mixin) dependent)
(setf (plist-value metaobject 'dependents)
(delete dependent (plist-value metaobject 'dependents))))
(defmethod map-dependents ((metaobject dependent-update-mixin) function)
(dolist (dependent (plist-value metaobject 'dependents))
(funcall function dependent)))